package com.atguigu.daijia.order.service.impl;

import com.atguigu.daijia.order.service.TestService;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @create: 2024/9/26
 * @author: gyhe10
 * @description: 测试本地锁和分布式锁
 **/

@Service
@RequiredArgsConstructor
public class TestServiceImpl implements TestService {

    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    public void testLock() {
        // 1 使用 Redisson 客户端创建锁对象
        RLock lock = redissonClient.getLock("lock");
        try {
            // 2 尝试获取锁
            // lock() 方法尝试获取锁时，默认阻塞时间为 -1，表示获取锁失败时会一直阻塞等待，直到获取锁，默认锁的过期时间为 30 秒，到期自动释放
            lock.lock();

            // lock() 方法可以通过传入的参数自定义锁的过期时间
            // lock.lock(10, TimeUnit.SECONDS);

        /*try {
            // tryLock() 方法，第一个参数表示获取锁失败的阻塞等待时间，第二个参数是如果获取到锁，锁的过期时间
            boolean result = lock.tryLock(20, 10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }*/

            // 3 获取锁成功，执行业务逻辑
            String value = redisTemplate.opsForValue().get("num");
            if (value == null) {
                return;
            }
            int num = Integer.parseInt(value);
            System.out.println("num = " + num);
            redisTemplate.opsForValue().set("num", String.valueOf(++num));
        } finally {
            // 4 释放锁
            lock.unlock();
        }
    }

    public void testLock0() {
        String value = redisTemplate.opsForValue().get("num");
        if (value == null) {
            return;
        }
        int num = Integer.parseInt(value);
        System.out.println("num = " + num);
        redisTemplate.opsForValue().set("num", String.valueOf(++num));
    }

    public synchronized void testLock1() {
        String value = redisTemplate.opsForValue().get("num");
        if (value == null) {
            return;
        }
        int num = Integer.parseInt(value);
        System.out.println("num = " + num);
        redisTemplate.opsForValue().set("num", String.valueOf(++num));
    }

    public void testLock2() {
        try {
            // 1 尝试获取锁
            Boolean result = redisTemplate.opsForValue().setIfAbsent("lock", "lock", 1, TimeUnit.SECONDS);
            if (result) {
                // 2 获取锁成功，执行业务逻辑
                String value = redisTemplate.opsForValue().get("num");
                if (value == null) {
                    return;
                }
                int num = Integer.parseInt(value);
                System.out.println("num = " + num);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));
            } else {
                // 3 获取锁失败，等待重试
                Thread.sleep(50);
                this.testLock();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 4 释放锁
            redisTemplate.delete("lock");
        }
    }

    public void testLock3() {
        String uuid = UUID.randomUUID().toString();
        try {
            // 1 尝试获取锁
            Boolean result = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 1, TimeUnit.SECONDS);
            if (result) {
                // 2 获取锁成功，执行业务逻辑
                String value = redisTemplate.opsForValue().get("num");
                if (value == null) {
                    return;
                }
                int num = Integer.parseInt(value);
                System.out.println("num = " + num);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));
            } else {
                // 3 获取锁失败，等待重试
                Thread.sleep(50);
                this.testLock();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (uuid.equals(redisTemplate.opsForValue().get("lock"))) {
                // 4 只能释放自己的锁
                redisTemplate.delete("lock");
            }
        }
    }

    public void testLock4() {
        String uuid = UUID.randomUUID().toString();
        try {
            // 1 尝试获取锁
            Boolean result = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 1, TimeUnit.SECONDS);
            if (result) {
                // 2 获取锁成功，执行业务逻辑
                String value = redisTemplate.opsForValue().get("num");
                if (value == null) {
                    return;
                }
                int num = Integer.parseInt(value);
                System.out.println("num = " + num);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));
            } else {
                // 3 获取锁失败，等待重试
                Thread.sleep(50);
                this.testLock();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // 使用 LUA 脚本确保判断锁的拥有者和释放锁是一个原子操作
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            // 设置脚本文本
            redisScript.setScriptText(script);
            // 设置返回类型
            redisScript.setResultType(Long.class);
            redisTemplate.execute(redisScript, Collections.singletonList("lock"), uuid);
        }
    }


}
